﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Security;
using System.Text;
using MuaChung.Base;

namespace MuaChung.Models
{
    public class CustomRoleProvider : RoleProvider
    {

        public override string[] GetRolesForUser(string username)
        {
            List<String> roles = new List<string>();
            roles.Add(AccountModel.GetRoleByUsername(username));
            return roles.ToArray();
        }

        public override void AddUsersToRoles(string[] usernames, string[] roleNames)
        {
            throw new NotImplementedException();
        }

        public override string ApplicationName
        {
            get
            {
                throw new NotImplementedException();
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        public override void CreateRole(string roleName)
        {
            throw new NotImplementedException();
        }

        public override bool DeleteRole(string roleName, bool throwOnPopulatedRole)
        {
            throw new NotImplementedException();
        }

        public override string[] FindUsersInRole(string roleName, string usernameToMatch)
        {
            throw new NotImplementedException();
        }

        public override string[] GetAllRoles()
        {
            throw new NotImplementedException();
        }

        public override string[] GetUsersInRole(string roleName)
        {
            throw new NotImplementedException();
        }

        public override bool IsUserInRole(string username, string roleName)
        {
            throw new NotImplementedException();
        }

        public override void RemoveUsersFromRoles(string[] usernames, string[] roleNames)
        {
            throw new NotImplementedException();
        }

        public override bool RoleExists(string roleName)
        {
            throw new NotImplementedException();
        }
    }
    #region Models
    [PropertiesMustMatch("NewPassword", "ConfirmPassword", ErrorMessage = "Xác nhận mật khẩu không chính xác.")]
    public class ChangePasswordModel
    {
        [Required]
        [DataType(DataType.Password)]
        [DisplayName("Mật khẩu hiện tại")]
        public string OldPassword { get; set; }

        [Required]
        [ValidatePasswordLength]
        [DataType(DataType.Password)]
        [DisplayName("Mật khẩu mới")]
        public string NewPassword { get; set; }

        [Required]
        [DataType(DataType.Password)]
        [DisplayName("Xác nhận mật khẩu mới")]
        public string ConfirmPassword { get; set; }
    }
    public class LogOnModel
    {
        [Required]
        [DisplayName("User name")]
        public string UserName { get; set; }

        [Required]
        [DataType(DataType.Password)]
        [DisplayName("Password")]
        public string Password { get; set; }

        [DisplayName("Remember me?")]
        public bool RememberMe { get; set; }
    }
    [PropertiesMustMatch("Password", "ConfirmPassword", ErrorMessage = "Xác nhận mật khẩu không chính xác.")]
    public class RegisterModel
    {
        [Required]
        [DisplayName("User name")]
        public string UserName { get; set; }

        [Required]
        [DataType(DataType.EmailAddress)]
        [DisplayName("Địa chỉ Email")]
        public string Email { get; set; }

        [Required]
        [ValidatePasswordLength]
        [DataType(DataType.Password)]
        [DisplayName("Password")]
        public string Password { get; set; }

        [Required]
        [DataType(DataType.Password)]
        [DisplayName("Confirm password")]
        public string ConfirmPassword { get; set; }

        [Required]
        [DataType(DataType.Text)]
        [DisplayName("Họ Tên")]
        public string HoTen { get; set; }

        [Required]
        [DataType(DataType.Date)]
        [DisplayName("Ngày Sinh")]
        public DateTime NgaySinh { get; set; }

        [Required]
        [DataType(DataType.PhoneNumber)]
        [DisplayName("Số điện thoại")]
        public string SDT { get; set; }

        [Required]
        [DataType(DataType.Text)]
        [DisplayName("Địa chỉ")]
        public string DiaChi { get; set; }

        [Required]
        [DataType(DataType.Text)]
        [DisplayName("Số chứng minh nhân dân")]
        public string CMND { get; set; }
    }
    public class ActivateModel
    {
        public int userID { get; set; }
        public string userName { get; set; }
        public string activatecode { get; set; }
        public string email { get; set; }
    }
    public class AccDetailModel
    {
        public int uid { get; set; }        
        public string status { get; set; }
        public string role { get; set; }

        [DisplayName("Ngày đăng ký")]
        public DateTime ngayDK { get; set; }
        [Required]
        [DisplayName("User name")]
        public string UserName { get; set; }

        [Required]
        [DataType(DataType.EmailAddress)]
        [DisplayName("Địa chỉ Email")]
        public string Email { get; set; }

        [Required]
        [DataType(DataType.Text)]
        [DisplayName("Họ Tên")]
        public string HoTen { get; set; }

        [Required]
        [DataType(DataType.Date)]
        [DisplayName("Ngày Sinh")]
        public DateTime NgaySinh { get; set; }

        [Required]
        [DataType(DataType.PhoneNumber)]
        [DisplayName("Số điện thoại")]
        public string SDT { get; set; }

        [Required]
        [DataType(DataType.Text)]
        [DisplayName("Địa chỉ")]
        public string DiaChi { get; set; }

        [Required]
        [DataType(DataType.Text)]
        [DisplayName("Số chứng minh nhân dân")]
        public string CMND { get; set; }
    }

    public class AccountModel
    {
        private static MuaChungEntities db = new MuaChungEntities();       
        public static string GetRoleByUsername(string usrname)
        {
            var query = from n in db.TAI_KHOAN
                        where n.Username == usrname
                        select n;
            try
            {
                return query.First<TAI_KHOAN>().Role;
            }
            catch
            {
                return null;
            }
        }
        public static TAI_KHOAN FindByID(int uid)
        {
            var query = from n in db.TAI_KHOAN
                        where n.ID == uid
                        select n;
            try
            {
                return query.First();
            }
            catch
            {
                return null;
            }
        }
        public static TAI_KHOAN FindByUsername(string usrname)
        {
            var query = from n in db.TAI_KHOAN
                        where n.Username == usrname
                        select n;
            try
            {
                return query.First();
            }
            catch
            {
                return null;
            }
        }
        public static TAI_KHOAN FindByEmail(string email)
        {
            var query = from n in db.TAI_KHOAN
                        where n.Email == email
                        select n;
            try
            {
                return query.First();
            }
            catch
            {
                return null;
            }
        }
        public static CHITIET_TAIKHOAN FindDetail(string usrname)
        {
            try
            {
                var query = from n in db.CHITIET_TAIKHOAN
                            where n.Username == usrname
                            select n;
                return query.First();
            }
            catch
            {
                return null;
            }
        }
        public static bool ValidateUser(string usrName, string password)
        {
            TAI_KHOAN tk = FindByUsername(usrName);
            if (tk == null)
            {
                return false;
            }
            if (BaseWeb.verifyMd5Hash(password,tk.Password))
            {                
                return true;
            }
            else
            {
                return false;
            }                        
        }
        public static bool ChangePassword(string usrName, string oldPassword, string newPassword)
        {
            try
            {
                TAI_KHOAN tmp = FindByUsername(usrName);
                if (!BaseWeb.verifyMd5Hash(oldPassword,tmp.Password))
                {
                    return false;
                }
                else
                {
                    tmp.Password = BaseWeb.getMd5Hash(newPassword);
                    db.TAI_KHOAN.ApplyCurrentValues(tmp);
                    db.SaveChanges();
                }
                return true;
            }
            catch
            {
                return false;
            }
        }
        public static MembershipCreateStatus AddUser(TAI_KHOAN usr, CHITIET_TAIKHOAN cttk)
        {
            if (FindByUsername(usr.Username) != null)
            {
                return MembershipCreateStatus.DuplicateUserName;
            }            
            if (FindByEmail(usr.Email) != null)
            {
                return MembershipCreateStatus.DuplicateEmail;
            }
            if (usr.Username.Contains('^'))
            {
                return MembershipCreateStatus.InvalidUserName;
            }
            try
            {
                usr.Password = BaseWeb.getMd5Hash(usr.Password);
                db.AddToTAI_KHOAN(usr);
                db.SaveChanges();

                cttk.Username = usr.Username;

                db.AddToCHITIET_TAIKHOAN(cttk);
                db.SaveChanges();
                return MembershipCreateStatus.Success;
            }
            catch
            {
                return MembershipCreateStatus.UserRejected;
            }
        }        
        public static bool UpdateAccountInfo(AccDetailModel model)
        {
            TAI_KHOAN tk = new TAI_KHOAN();
            try
            {
                var qtk = from n in db.TAI_KHOAN
                          where n.ID == model.uid
                          select n;
                tk = qtk.First();
                tk.Email = model.Email;
                if (model.role != null)
                {
                    tk.Role = model.role;
                }                
                db.TAI_KHOAN.ApplyCurrentValues(tk);
                db.SaveChanges();
            }
            catch
            { return false; }
            try
            {
                var qcttk = from n in db.CHITIET_TAIKHOAN
                            where n.Username == tk.Username
                            select n;
                CHITIET_TAIKHOAN cttk = qcttk.First();
                cttk.HoTen = model.HoTen;
                cttk.DienThoai = model.SDT;
                cttk.DiaChi = model.DiaChi;
                cttk.CMND = model.CMND;
                cttk.NgaySinh = model.NgaySinh;               

                db.CHITIET_TAIKHOAN.ApplyCurrentValues(cttk);
                db.SaveChanges();
            }
            catch
            { return false; }
            return true;
        }
        public static bool UpdateActivateCode(int uid,string code)
        {
            try
            {
                var tk2 = from n in db.TAI_KHOAN
                         where n.ID == uid
                         select n;
                TAI_KHOAN tktmp2 = tk2.First();
                var tk = from n in db.TAI_KHOAN
                         where n.Username == tktmp2.Username
                         select n;
                TAI_KHOAN tktmp = tk.First();
                CHITIET_TAIKHOAN tmp = AccountModel.FindDetail(tktmp.Username);
                tmp.Ma_KichHoat = code;
                db.CHITIET_TAIKHOAN.ApplyCurrentValues(tmp);
                db.SaveChanges();
                return true;
            }
            catch
            {
                return false;
            }
        }
        public static bool UpdateAccount(TAI_KHOAN tk)
        {
            try
            {
                db.TAI_KHOAN.ApplyCurrentValues(tk);
                db.SaveChanges();
                return true;
            }
            catch { return false; }
        }
        public static void UserLogin(string Username)
        {
            try
            {
                TAI_KHOAN tmp = FindByUsername(Username);
                tmp.SignedIn = 1;
                db.TAI_KHOAN.ApplyCurrentValues(tmp);
                db.SaveChanges();
            }
            catch
            {                
            }
        }
        public static void UserLogout(string Username)
        {
            try
            {
                TAI_KHOAN tmp = FindByUsername(Username);
                tmp.SignedIn = 0;
                db.TAI_KHOAN.ApplyCurrentValues(tmp);
                db.SaveChanges();
            }
            catch
            {
            }
        }
        public static void ActivateUser(string userName)
        {
            TAI_KHOAN tmp = FindByUsername(userName);
            tmp.Status = "Activated";
            db.TAI_KHOAN.ApplyCurrentValues(tmp);
            db.SaveChanges();
        }
        public static List<TAI_KHOAN> GetAll(int currentPage = 1, int pageSize = 10)
        {
            int startindex = pageSize * (currentPage - 1);
            int endindex = pageSize * currentPage;
            try
            {
                var query = (from n in db.TAI_KHOAN
                             orderby n.ID descending
                             select n).Skip(startindex);
                if (query.Count() > 0)
                {
                    return query.Take(pageSize).ToList();
                }
                else
                {
                    return null;
                }
            }
            catch { return null; }  
        }
        public static bool DeleteAccount(int id)
        {
            string usernam = "";
            try
            {
                var tk = from n in db.TAI_KHOAN
                         where n.ID == id
                         select n;
                TAI_KHOAN tktmp = tk.First();
                usernam = tktmp.Username;
                if (tktmp.TROGIUPs.Count>0)
                {
                    foreach (var item in tktmp.TROGIUPs)
                    {
                        TROGIUP tmp = new TROGIUP();
                        tmp.Email = item.Email;
                        tmp.ID = item.ID;
                        tmp.NgayHoi = item.NgayHoi;
                        tmp.NoiDung = item.NoiDung;
                        tmp.TinhTrang = 0;
                        HelpModel.UpdateQuestion(tmp);
                    }
                }
                db.TAI_KHOAN.DeleteObject(tk.First());
                db.SaveChanges();
                return true;
            }

            catch {  }
            try
            {                
                var cttk = from n in db.CHITIET_TAIKHOAN
                           where n.Username == usernam
                           select n;

                db.CHITIET_TAIKHOAN.DeleteObject(cttk.First());
                return true;
            }
            catch { return false; }
        }
        public static int GetNumberAccount()
        {
            try
            {
                var query = from n in db.TAI_KHOAN
                            select n;
                return query.Count();
            }
            catch
            {
                return 0;
            }
        }
    }

    #endregion

    #region Services
    // The FormsAuthentication type is sealed and contains static members, so it is difficult to
    // unit test code that calls its members. The interface and helper class below demonstrate
    // how to create an abstract wrapper around such a type in order to make the AccountController
    // code unit testable.

    public interface IMembershipService
    {
        int MinPasswordLength { get; }

        bool ValidateUser(string userName, string password);
        MembershipCreateStatus CreateUser(string userName, string password, string email, CHITIET_TAIKHOAN cttk);
        bool ChangePassword(string userName, string oldPassword, string newPassword);
    }

    public class AccountMembershipService : IMembershipService
    {
        private readonly MembershipProvider _provider;

        public AccountMembershipService()
            : this(null)
        {
        }

        public AccountMembershipService(MembershipProvider provider)
        {
            _provider = provider ?? Membership.Provider;
        }

        public int MinPasswordLength
        {
            get
            {
                return _provider.MinRequiredPasswordLength;
            }
        }

        public bool ValidateUser(string userName, string password)
        {
            if (String.IsNullOrEmpty(userName)) throw new ArgumentException("Khổng thể để trống thông tin này!", "userName");
            if (String.IsNullOrEmpty(password)) throw new ArgumentException("Khổng thể để trống thông tin này!", "password");

            return AccountModel.ValidateUser(userName, password);
        }

        public MembershipCreateStatus CreateUser(string userName, string password, string email,CHITIET_TAIKHOAN cttk)
        {
            if (String.IsNullOrEmpty(userName)) throw new ArgumentException("Khổng thể để trống thông tin này!", "userName");
            if (String.IsNullOrEmpty(password)) throw new ArgumentException("Khổng thể để trống thông tin này!", "password");
            if (String.IsNullOrEmpty(email)) throw new ArgumentException("Khổng thể để trống thông tin này!", "email");
            

            TAI_KHOAN tk = new TAI_KHOAN();
            tk.Username = userName;
            tk.Password = password;
            tk.Email = email;
            tk.Role = "Customer";
            tk.SignedIn = 0;
            tk.Status = "Inactive";


            return AccountModel.AddUser(tk, cttk);            
        }

        public bool ChangePassword(string userName, string oldPassword, string newPassword)
        {            
            if (String.IsNullOrEmpty(oldPassword)) throw new ArgumentException("Khổng thể để trống thông tin này!", "oldPassword");
            if (String.IsNullOrEmpty(newPassword)) throw new ArgumentException("Khổng thể để trống thông tin này!", "newPassword");

            // The underlying ChangePassword() will throw an exception rather
            // than return false in certain failure scenarios.
            try
            {
                return AccountModel.ChangePassword(userName, oldPassword, newPassword);
            }
            catch (ArgumentException)
            {
                return false;
            }
            catch (MembershipPasswordException)
            {
                return false;
            }
        }
    }

    public interface IFormsAuthenticationService
    {
        void SignIn(string userName, bool createPersistentCookie);
        void SignOut(string userName);
    }

    public class FormsAuthenticationService : IFormsAuthenticationService
    {
        public void SignIn(string userName, bool createPersistentCookie)
        {
            if (String.IsNullOrEmpty(userName)) throw new ArgumentException("Khổng thể để trống thông tin này!", "userName");

            AccountModel.UserLogin(userName);

            FormsAuthentication.SetAuthCookie(userName, createPersistentCookie);
        }

        public void SignOut(string username)
        {
            AccountModel.UserLogout(username);
            FormsAuthentication.SignOut();
        }
    }
    #endregion

    #region Validation
    public static class AccountValidation
    {
        public static string ErrorCodeToString(MembershipCreateStatus createStatus)
        {
            // See http://go.microsoft.com/fwlink/?LinkID=177550 for
            // a full list of status codes.
            switch (createStatus)
            {
                case MembershipCreateStatus.DuplicateUserName:
                    return "Username này đã tồn tại. Vui lòng chọn một username khác!";

                case MembershipCreateStatus.DuplicateEmail:
                    return "Email này đã được sử dụng. Vui lòng chọn email khác!";

                case MembershipCreateStatus.InvalidPassword:
                    return "Mật khẩu của bạn không hợp lệ. Vui lòng nhập mật khẩu khác!";

                case MembershipCreateStatus.InvalidEmail:
                    return "Email này không hợp lệ. Vui lòng kiểm tra và thử lại!";

                case MembershipCreateStatus.InvalidAnswer:
                    return "The password retrieval answer provided is invalid. Please check the value and try again.";

                case MembershipCreateStatus.InvalidQuestion:
                    return "The password retrieval question provided is invalid. Please check the value and try again.";

                case MembershipCreateStatus.InvalidUserName:
                    return "Username của bạn không hợp lệ. Vui lòng kiểm tra và thử lại!";

                case MembershipCreateStatus.ProviderError:
                    return "The authentication provider returned an error. Please verify your entry and try again. If the problem persists, please contact your system administrator.";

                case MembershipCreateStatus.UserRejected:
                    return "Đăng ký tài khoản thất bại, vui lòng kiểm tra thông tin và thử lại. Nếu vẫn không đăng ký được, vui lòng liên hệ với người quản trị hệ thống.";

                default:
                    return "Đã xảy ra lỗi không biết. Vui lòng kiểm tra thông tin và thử lại. Nếu vẫn không đăng ký được, vui lòng liên hệ với người quản trị hệ thống.";
            }
        }
    }

    [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
    public sealed class PropertiesMustMatchAttribute : ValidationAttribute
    {
        private const string _defaultErrorMessage = "'{0}' and '{1}' do not match.";
        private readonly object _typeId = new object();

        public PropertiesMustMatchAttribute(string originalProperty, string confirmProperty)
            : base(_defaultErrorMessage)
        {
            OriginalProperty = originalProperty;
            ConfirmProperty = confirmProperty;
        }

        public string ConfirmProperty { get; private set; }
        public string OriginalProperty { get; private set; }

        public override object TypeId
        {
            get
            {
                return _typeId;
            }
        }

        public override string FormatErrorMessage(string name)
        {
            return String.Format(CultureInfo.CurrentUICulture, ErrorMessageString,
                OriginalProperty, ConfirmProperty);
        }

        public override bool IsValid(object value)
        {
            PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(value);
            object originalValue = properties.Find(OriginalProperty, true /* ignoreCase */).GetValue(value);
            object confirmValue = properties.Find(ConfirmProperty, true /* ignoreCase */).GetValue(value);
            return Object.Equals(originalValue, confirmValue);
        }
    }

    [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
    public sealed class ValidatePasswordLengthAttribute : ValidationAttribute
    {
        private const string _defaultErrorMessage = "'{0}' must be at least {1} characters long.";
        private readonly int _minCharacters = Membership.Provider.MinRequiredPasswordLength;

        public ValidatePasswordLengthAttribute()
            : base(_defaultErrorMessage)
        {
        }

        public override string FormatErrorMessage(string name)
        {
            return String.Format(CultureInfo.CurrentUICulture, ErrorMessageString,
                name, _minCharacters);
        }

        public override bool IsValid(object value)
        {
            string valueAsString = value as string;
            return (valueAsString != null && valueAsString.Length >= _minCharacters);
        }
    }
    #endregion

}
